package com.demo.lock;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * Created by hunter
 *
 * @Description 一个简单的悲观锁Demo, 类如更新数据, 将所有的数据锁起来不让读, 等更新完成后再释放, 不管更新的数据是否为需要查询的数据, 悲观的认为所有的数据都是正在被修改中或被查询中
 *              具体应用像MYSQL的表锁
 */
public class PessimisticLock {

    // 当做一个DB
    private static final HashMap<Object, Object> DB_MAP = new HashMap<>();

    // 填充DB数据
    static {
        DB_MAP.put(1, 1);
        DB_MAP.put(2, 2);
        DB_MAP.put(3, 3);
        DB_MAP.put(4, 4);
        DB_MAP.put(5, 5);
    }

    /**
     * 更新方法, 与自身再次调用和查询方法调用互斥
     *
     * @param key
     * @param value
     */
    public synchronized void update(Object key, Object value) {

        // 模拟数据库延迟
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Update method begin ... key : " + key + ", value : " + value);

        // 模拟DB更新操作
        DB_MAP.put(key, value);
    }

    /**
     * 查询方法, 与自身再次调用和更新方法调用互斥
     *
     * @param key
     * @return
     */
    public synchronized Object get(Object key) {

        // 模拟数据库延迟
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Object value = DB_MAP.get(key);
        System.out.println("Get method end, key is : " + key + ", value is : " + value);

        return value;
    }

    public static void main(String[] args) {
        PessimisticLock lockTest = new PessimisticLock();

        // 模拟高并发查询请求, 只查询5的数据
        for (int i = 0; i < 40; i++) {
            new Thread(() -> lockTest.get(5)).start();
        }

        // 模拟高并发更新请求, 当能整除5时去更新5, 否则更新其它数据, 以此来对比查询操作
        for (int i = 0; i < 20; i++) {
            final int key = (i % 5 == 0) ? 5 : i;
            final int value = i;
            new Thread(() -> lockTest.update(key, value)).start();
        }

        // 可以看到当更新操作时, 所有的查询操作会被锁住, 当查询操作时更新操作也被锁住, 判断的依据是打印秒数间隔, 悲观的不行
    }
}
